<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Time Stretcher</title>
    </head>
    <body>
        <p>
        Instructions:  Tim, click the "Browse..." or "Choose File" button.  Select an audio file and click Open.  Click the "Play 3 seconds at 25%." button.  Enjoy.  That is it.  Probably won't work for you.  I've had success with all files I've tested in Firefox, but Chrome doesn't appear to like all files.
        </p>
        <input type="file" id="file" />
<div id="panLoad">Loading...</div>
        <input type="button" id="btnPlay" style="display:none;" onclick="playSlow(5);" value="Play 3 seconds at 25%." />

        <script>
//http://www.html5rocks.com/en/tutorials/webaudio/intro/
//why does mozilla say the playback rate is muted for "unuseful" ranges?
//https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement

var context;
var soundBuffer = null;
var source;

window.addEventListener('load', init, false);
document.getElementById('file').addEventListener('change', handleFileSelect, false);

function handleFileSelect(evt) {
    var file = evt.target.files[0]; // FileList object

    document.getElementById('panLoad').style.display = "inherit";

    var reader = new FileReader();
    reader.onload = function(evt) {
        console.log('foo');
        context.decodeAudioData(evt.target.result, function(buffer) {
                    soundBuffer = buffer;
                    console.log(soundBuffer);
                    document.getElementById('file').style.display = "none";
                    document.getElementById('btnPlay').style.display = "";
                    document.getElementById('panLoad').style.display = "none";
        }, function() {
            console.log('error', arguments);
        });
    }
    reader.readAsArrayBuffer(file);
}





function loadSound(url, onError) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
        context.decodeAudioData(request.response, function(buffer) {
            soundBuffer = buffer;
            console.log(soundBuffer);
        }, onError);
    }
    request.send();
}





function play(rate) {
    source = context.createBufferSource(); // creates a sound source

    //source = context.createMediaElementSource(document.getElementsByTagName("audio")[0]);
    source.buffer = soundBuffer;                    // tell the source which sound to play

    source.connect(context.destination);       // connect the source to the context's destination (the speakers)

    source.playbackRate.value = rate || 1;
    source.start(0);                           // when, offset, duration
}


function stop() {
    source.stop();
}



function playSlow(xFactor) {
    xFactor = xFactor || 10;
    var timeSlice = .01;  //seconds, repeat each timeSlice xFactor times

    for (var i = 0; i < 300; ++i) {  //one loop represents one time slice
        for (var j = 0; j < xFactor; ++j) {
            source = context.createBufferSource(); // creates a sound source
            source.buffer = soundBuffer;                    // tell the source which sound to play
            source.connect(context.destination);       // connect the source to the contexts destination (the speakers)
            //console.log("when", timeSlice * (i * xFactor + j), "offset", i * timeSlice, "duration", timeSlice);
            source.start(context.currentTime + timeSlice * (i * xFactor + j), i * timeSlice, timeSlice);
        }
    }
}


/*
 * x - number - speed to play at, e.g. 1 is normal. .5 is half, .25 is quarter, 2 is double
 * ts - number - time segment (in seconds) to repeat/phase/???. e.g. .1 would cause tenth of a second sections to be played
 */
function playSlow2(x, ts) {
    for (var i = 0; i < 3 / ts /*TODO temp code to just play first 3 seconds*/; ++i) {  //one loop represents one time slice
        for (var j = 0; j < 1 / x  /* TODO do i have to assume ts === 2^n? */; ++j) {
            source = context.createBufferSource(); // creates a sound source
            source.buffer = soundBuffer;                    // tell the source which sound to play
            source.connect(context.destination);       // connect the source to the contexts destination (the speakers)
            console.log("when", ts * (i * 1/x + j), "offset", i * ts + j * x * ts, "duration", ts);
            //source.start(context.currentTime + ts * (i * 1/x + j), i * ts + j * x * ts, ts);
        }
    }
}



function init() {
    try {
        window.AudioContext = window.AudioContext||window.webkitAudioContext;
        context = new AudioContext();

        //loadSound('Maid with the Flaxen Hair.mp3', function() { console.log('error', arguments); });
    }
    catch(e) {
        alert('Web Audio API is not supported in this browser');
    }
}


        </script>
        <style>
            body{margin:0;padding:1em;font-family:sans-serif;}
            #panLoad{background:#09c;color:#fff;font-size:5em;position:fixed;top:0;bottom:0;left:0;right:0;text-align:center;display:none;}
        </style>
    </body>
</html>